import java.rmi.* ; 
import java.rmi.server.UnicastRemoteObject ;
import java.net.MalformedURLException ; 
import java.util.Hashtable;
import java.util.*;
import javax.media.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import java.rmi.RemoteException ;

public class Client_Impl extends UnicastRemoteObject implements Client_Itf 
{
	int id;					 //sert de capacite
	Hashtable listeVoisins = new Hashtable();
	int idPremierEmetteurJeton;
	int idElecteur;
	Serveur_Itf s;
	String ip;
	String file;
	String fileDefaut;
	String port;
	JFrame fenetre;
	String[] sessions;
	
	JButton lister;
	JButton videoDefaut;	
	JButton video;
	JButton message;	
	
	JTextArea screen;
	JTextArea saisie;
	
	class ThreadServeurStreaming extends Thread 
	{
		  ThreadServeurStreaming() 
		  {}

		  public void run() 
		  {		  						
				// Create a video transmit object with the specified params.
				VideoTransmit vt = new VideoTransmit(new MediaLocator(file), ip, port);
		
				// Start the transmission
				String result = vt.start();
				screen.append("\nTransmission de la vidéo.");

				if (result != null) 
				{
					System.err.println("Error : " + result);

					try
					{
						messageErr(id,new Hashtable(), id, idElecteur); 
					}
					catch (RemoteException e)
					{}
					
					return;
				}

				System.err.println("Start transmission for 60 seconds...");
	
				try 
				{
					Thread.currentThread().sleep(60000);
				} 
				catch (InterruptedException ie) 
				{}

				// Stop the transmission
				vt.stop();

				System.err.println("...transmission ended.");	
				screen.append("\nFin de la transmission.");
		  }
	}
	
	class ThreadClientStreaming extends Thread 
	{
		  ThreadClientStreaming() 
		  {}

		  public void run() 
		  {		  		
				AVReceive2 avReceive = new AVReceive2(sessions);
		
				screen.append("\nRéception de la vidéo...");
						
				if (!avReceive.initialize()) 
				{
					System.err.println("Failed to initialize the sessions.");
					System.exit(-1);
				}

				// Check to see if AVReceive2 is done.
				try 
				{
					while (!avReceive.isDone())
					Thread.sleep(1000);
				} 
				catch (Exception e) 
				{}

				System.err.println("Exiting AVReceive2");	
		  }
	}
	
	public Client_Impl (String adServeur, String adip, String adport, String adfile) throws RemoteException 
	{
		try 
		{		
			s =(Serveur_Itf) Naming.lookup("rmi://"+ adServeur +"/Serveur");

			id = s.addList(this);
			
			initFenetre(id);
			fenetre.setVisible(true);
			screen.append("\nJe suis le client " + id);
			
			s.getVoisins(id);
			
			ip = adip;
			fileDefaut = adfile;
			port = adport;
		}
		catch (NotBoundException re) 
		{ 
			screen.append("\n" + re); 
		}
		catch (RemoteException re) 
		{ 
			screen.append("\n" + re); 
		}
		catch (MalformedURLException e) 
		{ 
			screen.append("\n" + e); 
		}
	}


	public void addVoisinConnection(Client_Itf cNouveauVoisin, int idNouveauVoisin) throws RemoteException
	{
		try
		{	
			String cle = "" + idNouveauVoisin;
			listeVoisins.put(cle, cNouveauVoisin);
			screen.append("\nMon voisin : " + idNouveauVoisin);

			cNouveauVoisin.addVoisin(this, id);
		}
		catch (NullPointerException re) 
		{}
	}
	
	
	public void addVoisin(Client_Itf cNouveauConnecte, int idNouveauConnecte) throws RemoteException
	{
		String cle = "" + idNouveauConnecte;
		listeVoisins.put(cle, cNouveauConnecte);
		screen.append("\nNouveau voisin : " + idNouveauConnecte);
	}
	
	
	public void election(int idEmetteur, int capacite, int idCapacite, Hashtable liste, int idInit, int type, String url) throws RemoteException
	{	
		if(liste.containsKey("" + id)) 					// si on est dans la liste, cela signifie que l'on a déjà reçu le jeton
		{			
			int nextVoisin = trouverVoisin(liste);
			
			if(nextVoisin != -1) 						// si on a encore un voisin qui n'a pas recu le jeton
			{
				((Client_Itf) listeVoisins.get("" + nextVoisin)).election(id, capacite, idCapacite, liste, idInit, type, url);	// on lui envoie
			}
			else 										// s'il n'y en a pas, on renvoie à celui qui nous l'a envoyé
			{
				if(id == idInit) 						// si le jeton est au point de départ
				{
					if(type == 0)						// si election
					{
						election(id, 0, idCapacite,new Hashtable(), id, 1, url);	// 1 : information -> informer le mec élu qu'il est élu
					}
					else
					{
						
						screen.append("\n" + "J'ai élu "+ idCapacite +"!");
						String session = s.iReceive() + "/60"; //<ip>/<port>/<ttl>
						sessions =new String[1];
						sessions[0] = session;
		  				ThreadClientStreaming p = new ThreadClientStreaming();		// on lance le flux
						p.start();			
					}
				}
				else										// sinon on l'envoie au précédent
				{
					((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).election(id, capacite, idCapacite, liste, idInit, type, url);
				}
			}			
		}
		else 													// sinon c'est la premiere fois qu'on reçoit le jeton
		{		
			idPremierEmetteurJeton = idEmetteur;
			liste.put(""+id,this);								// on s'ajoute dans la liste
			
			if(type == 0)										// si election
			{
				capacite = capacite > id ? capacite : id; 		// capacite max
				idCapacite = capacite > id ? idCapacite : id;	// id de la capacite max
			}
			else												// sinon information
			{
				if(idCapacite == id)
				{
					screen.append("\n" + "J'ai été élu par " + idInit + " !");
					idElecteur = idInit;
					s.iSend(ip,port);		
					file = url;			
					ThreadServeurStreaming p = new ThreadServeurStreaming();		// on lance le flux
					p.start();
				}

				if(idCapacite != id && id != idInit)
				{
					screen.append("\n" + "Je sais que " + idInit + " a élu "+ idCapacite +"!");
				}

			}
		
			int nextVoisin = trouverVoisin(liste);
			
			if(nextVoisin != -1) 								// si on a encore un voisin qui n'a pas recu le jeton
			{
				((Client_Itf) listeVoisins.get("" + nextVoisin)).election(id, capacite, idCapacite, liste, idInit, type, url);	// on lui envoie
			}
			else 												// s'il n'y en a pas, on renvoit à celui qui nous l'a envoyé
			{
				if(id == idInit)
				{
					this.election(id, capacite, idCapacite, liste, idInit, type, url);
				}
				else
				{
					((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).election(id, capacite, idCapacite, liste, idInit, type, url);
				}
			}
		}
	}
	
	
	public void messageAll(int idEmetteur, Hashtable liste, int idInit, String message) throws RemoteException
	{	
		if(liste.containsKey("" + id)) 					// si on est dans la liste, cela signifie que l'on a déjà reçu le jeton
		{			
			int nextVoisin = trouverVoisin(liste);
			
			if(nextVoisin != -1) 						// si on a encore un voisin qui n'a pas recu le jeton
			{
				((Client_Itf) listeVoisins.get("" + nextVoisin)).messageAll(id, liste, idInit,message);	// on lui envoie
			}
			else 										// s'il n'y en a pas, on renvoie à celui qui nous l'a envoyé
			{
				if(id == idInit) 						// si le jeton est au point de départ
				{
					screen.append("\n" + "Message propagé");					//fin de transmission
				}
				else									// sinon on l'envoie au précédent
				{
					((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).messageAll(id, liste, idInit,message);
				}
			}			
		}
		else 													// sinon c'est la premiere fois qu'on reçoit le jeton
		{		
			idPremierEmetteurJeton = idEmetteur;
			liste.put(""+id,this);								// on s'ajoute dans la liste
			
			if(id != idInit)
			{
				screen.append("\nMessage reçu du client " + idInit + " : " + message);
			}
			
			int nextVoisin = trouverVoisin(liste);
			
			if(nextVoisin != -1) 								// si on a encore un voisin qui n'a pas recu le jeton
			{
				((Client_Itf) listeVoisins.get("" + nextVoisin)).messageAll(id, liste, idInit,message);	// on lui envoie
			}
			else 												// s'il n'y en a pas, on renvoit à celui qui nous l'a envoyé
			{
				if(id == idInit)
				{
					this.messageAll(id, liste, idInit,message);
				}
				else
				{
					((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).messageAll(id, liste, idInit,message);
				}
			}
		}
	}
	
	public void messageErr(int idEmetteur, Hashtable liste, int idInit, int idDest) throws RemoteException
	{	
		if(liste.containsKey("" + id)) 					// si on est dans la liste, cela signifie que l'on a déjà reçu le jeton
		{			
			int nextVoisin = trouverVoisin(liste);
			
			if(nextVoisin != -1) 						// si on a encore un voisin qui n'a pas recu le jeton
			{
				((Client_Itf) listeVoisins.get("" + nextVoisin)).messageErr(id, liste, idInit,idDest);	// on lui envoie
			}
			else 										// s'il n'y en a pas, on renvoie à celui qui nous l'a envoyé
			{
				if(id == idInit) 						// si le jeton est au point de départ
				{
					screen.append("\nMessage propagé.");					//fin de transmission
				}
				else									// sinon on l'envoie au précédent
				{
					((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).messageErr(id, liste, idInit,idDest);
				}
			}			
		}
		else 													// sinon c'est la premiere fois qu'on reçoit le jeton
		{		
			idPremierEmetteurJeton = idEmetteur;
			liste.put(""+id,this);								// on s'ajoute dans la liste
			
			if(id == idDest)
			{
				screen.append("\nMessage reçu du client " + idInit + " : Mauvais fichier vidéo");
			}
			else
			{
				int nextVoisin = trouverVoisin(liste);
			
				if(nextVoisin != -1) 								// si on a encore un voisin qui n'a pas recu le jeton
				{
					((Client_Itf) listeVoisins.get("" + nextVoisin)).messageErr(id, liste, idInit,idDest);	// on lui envoie
				}
				else 												// s'il n'y en a pas, on renvoit à celui qui nous l'a envoyé
				{
					if(id == idInit)
					{
						this.messageErr(id, liste, idInit,idDest);
					}
					else
					{
						((Client_Itf) listeVoisins.get("" + idPremierEmetteurJeton)).messageErr(id, liste, idInit,idDest);
					}
				}
			}
		}
	}
	
	
	public int trouverVoisin(Hashtable liste) throws RemoteException
	{
		boolean trouve = false;
		int retour=-1;
		
		for(Enumeration e = listeVoisins.keys(); e.hasMoreElements();)
		{
			String cle = (String)e.nextElement();
			
			if(!trouve)
			{
				if(!liste.containsKey(cle))
				{
					trouve = true;
					retour = Integer.parseInt(cle);
				}
			} 
		}
		return retour;
	}
	
	
	private void clientStreaming (String[] sessions)
	{
		AVReceive2 avReceive = new AVReceive2(sessions);
		
		screen.append("\nRéception de la vidéo...");
						
		if (!avReceive.initialize()) 
		{
			System.err.println("Failed to initialize the sessions.");
			System.exit(-1);
		}

		// Check to see if AVReceive2 is done.
		try 
		{
			while (!avReceive.isDone())
			Thread.sleep(1000);
		} 
		catch (Exception e) 
		{}

		System.err.println("Exiting AVReceive2");	
	}

	
	public void initFenetre(int i)
	{		
		fenetre = new JFrame();
	
		JPanel body = new JPanel();
		JPanel haut = new JPanel();
		JPanel bas = new JPanel();
		JPanel vHaut = new JPanel();
		JPanel vBas = new JPanel();
		JPanel pScreen = new JPanel();
		JPanel pLister = new JPanel();
		JPanel pVideoDefaut = new JPanel();
		JPanel pSaisie = new JPanel();
		JPanel pVideo = new JPanel();
		JPanel pMessage = new JPanel();
		JPanel container = new JPanel();
		JPanel margeG = new JPanel();
		JPanel margeD = new JPanel();
		JPanel margeH = new JPanel();
		JPanel margeB = new JPanel();
	
		lister = new JButton("Lister voisins");
		videoDefaut = new JButton("Lancer vidéo /défaut");
		video = new JButton("Lancer vidéo");
		message = new JButton("Envoyer Message");
		
		screen = new JTextArea();
		saisie = new JTextArea();
		
		//frame
		fenetre.setResizable(false); 
		fenetre.setTitle("Client " + i);
		fenetre.setFont(new java.awt.Font("Bookman Old Style",0,14));
		fenetre.setLocationRelativeTo(null);
		fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		fenetre.setSize(new Dimension(520, 420));
		
		//grid layout
		GridLayout glVertical = new GridLayout(2,1);
		GridLayout glHorizontal = new GridLayout(1,2);
		GridLayout glFull = new GridLayout(1,1);
		
		//panel
		body.setLayout(new BorderLayout());
		body.setPreferredSize(new Dimension(500,400));
		haut.setLayout(glHorizontal);
		bas.setLayout(glHorizontal);
		vHaut.setLayout(glVertical);
		vBas.setLayout(glVertical);
		pScreen.setLayout(glFull);
		container.setLayout(new BorderLayout());
		margeG.setPreferredSize(new Dimension(10,400));
		margeD.setPreferredSize(new Dimension(10,400));
		margeH.setPreferredSize(new Dimension(500,10));
		margeB.setPreferredSize(new Dimension(500,10));
		
		//button
		lister.addActionListener(new ListerListener());
		lister.setPreferredSize(new Dimension(175,35));
		lister.setFont(new java.awt.Font("Verdana",0,12));
		videoDefaut.addActionListener(new VideoDefautListener());
		videoDefaut.setPreferredSize(new Dimension(175,35));
		videoDefaut.setFont(new java.awt.Font("Verdana",0,12));
		message.addActionListener(new MessageListener());
		message.setPreferredSize(new Dimension(175,35));
		message.setFont(new java.awt.Font("Verdana",0,12));
		video.addActionListener(new VideoListener());
		video.setPreferredSize(new Dimension(175,35));
		video.setFont(new java.awt.Font("Verdana",0,12));
		
		//TextArea screen
		screen.append("");
		screen.setEnabled(false);
		screen.setLineWrap(true);
		screen.setForeground(Color.magenta);
		screen.setFont(new java.awt.Font("Verdana",0,14));
		screen.setDisabledTextColor(new java.awt.Color(50,85,100));
		screen.setOpaque(true);			
		screen.setBackground(Color.WHITE);
		screen.setAlignmentY(SwingConstants.TOP);
		screen.setPreferredSize(new Dimension(225,200));
		
		//TextArea saisie
		saisie.append("");
		saisie.setEnabled(true);
		saisie.setLineWrap(true);
		saisie.setForeground(Color.magenta);
		saisie.setFont(new java.awt.Font("Verdana",0,14));
		saisie.setDisabledTextColor(new java.awt.Color(50,85,100));
		saisie.setOpaque(true);			
		saisie.setBackground(Color.WHITE);
		saisie.setAlignmentY(SwingConstants.TOP);
		saisie.setAlignmentX(SwingConstants.CENTER);
		saisie.setPreferredSize(new Dimension(225,35));
		
		//add
		fenetre.setContentPane(container);
		container.add(body);
		container.add(margeB,BorderLayout.SOUTH);
		container.add(margeG,BorderLayout.WEST);
		container.add(margeH,BorderLayout.NORTH);
		container.add(margeD,BorderLayout.EAST);	
		body.add(haut);
		haut.add(pScreen);
		pScreen.add(screen);
		haut.add(vHaut);
		vHaut.add(pLister);
		pLister.add(lister);
		vHaut.add(pVideoDefaut);
		pVideoDefaut.add(videoDefaut);
		body.add(bas,BorderLayout.SOUTH);
		bas.add(pSaisie);
		pSaisie.add(saisie);
		bas.add(vBas);
		vBas.add(pVideo);
		pVideo.add(video);
		vBas.add(pMessage);
		pMessage.add(message);	
	}
	
	
	public class ListerListener implements ActionListener
	{
		public void actionPerformed(ActionEvent arg0) 
		{   
			screen.append("\n\n** Liste des voisins :");
			for(Enumeration e = listeVoisins.keys(); e.hasMoreElements();)
			{
				String cle = (String)e.nextElement();
				screen.append("\n   Client " + cle);				
			}
		}
	}	
	
	public class VideoDefautListener implements ActionListener
	{
		public void actionPerformed(ActionEvent arg0)
		{   
			try
			{
				screen.append("\nVous avez lancé l'élection.");
						
				election(id,id, id,new Hashtable(), id, 0, fileDefaut);		// 0 = election
				saisie.setText("");
			}
			catch(RemoteException e)
			{}
		}
	}	
	
	public class MessageListener implements ActionListener
	{
		public void actionPerformed(ActionEvent arg0)
		{   
			try
			{
				messageAll(id,new Hashtable(), id, saisie.getText()); 
				saisie.setText("");
			}
			catch(RemoteException e)
			{}
		}
	}	
	
	public class VideoListener implements ActionListener
	{
		public void actionPerformed(ActionEvent arg0)
		{   
			try
			{
				screen.append("\nVous avez lancé l'élection.");
				
				election(id,id, id,new Hashtable(), id, 0, saisie.getText());		// 0 = election
				saisie.setText("");
			}
			catch(RemoteException e)
			{}
		}
	}
}

